
package boids;

import processing.core.PGraphics;
import toxi.geom.Vec2D;

public class Boid2D extends Vehicle2D implements ProcessingRenderable
{

	private int					mode;

	private Environment2D	environment;
	private Vec2D				target;

	private boolean			avoidObstacles	= false;
	private float				vSize				= 10.0f;

	public Boid2D()
	{
		super(new Vec2D());

		setDefaultProperties();
		setDefaultCollisionBox();
		
		target = new Vec2D(getPosition());
	}

	public Boid2D(Vec2D _pos)
	{
		super(_pos);

		setDefaultProperties();
		setDefaultCollisionBox();
		
		target = new Vec2D(getPosition());
	}

	public void setMode(int _mode)
	{
		mode = _mode;
		
		if (mode == PATHFOLLOW)
		{
			Path pathToFollow = getPathToFollow();
			if (pathToFollow != null)
			{
				setPathFollowIndexToClosestPoint(pathToFollow);
			}
		}
	}
	
	public int getMode()
	{
		return mode;
	}
	
	public SplinePath getPathToFollow()
	{
		SplinePath pathToFollow = environment.getPathForBoid(this);
		if (pathToFollow == null)
		{
			pathToFollow = environment.getGlobalPath();
		}
		return pathToFollow;
	}

	public void update()
	{
		switch (mode)
		{
			case WANDER:
				wander();
				break;
			case SEEK:
				seek(target);
				break;
			case ARRIVE:
				arrive(target);
				break;
			case AVOID:
				avoid(target);
				break;
			case FLEE:
				flee(target);
				break;
			case PATHFOLLOW:
				SplinePath pathToFollow = getPathToFollow();
				if (pathToFollow != null)
				{
					followPath(pathToFollow, false, false);
				}
				break;
		}

		if (avoidObstacles && environment != null)
		{
			avoidObstacles(environment.getObstacles());
		}
		
		if (mode != NONE)
		{
			move();
		}
	}

	public void toggleAvoidObstacles()
	{
		avoidObstacles = !avoidObstacles;
	}

	public boolean avoidsObstacles()
	{
		return avoidObstacles;
	}

	public Vec2D getTarget()
	{
		return target;
	}

	public void setTarget(Vec2D _target)
	{
		target = _target;
	}

	public void setTarget(float _x, float _y)
	{
		target.set(_x, _y);
	}

	public void setEnvironment(Environment2D _environment)
	{
		environment = _environment;
	}
	
	public Environment2D getEnvironment()
	{
		return environment;
	}

	public void render(PGraphics _g)
	{
		switch (mode)
		{
			case WANDER:
				renderWander(_g);
				break;
			case SEEK:
				renderApproachTarget(_g);
				break;
			case ARRIVE:
				renderApproachTarget(_g);
				break;
			case FLEE: 
				renderApproachTarget(_g);
				break;
			case PATHFOLLOW:
				renderPathFollow(_g);
				break;
			default: 
				renderApproachTarget(_g);
		}
	}
	
	public void renderPathFollow(PGraphics _g)
	{
		_g.noStroke();
		_g.fill(255.0f);
		_g.pushMatrix();
		_g.translate(getPosition().x, getPosition().y);
		_g.rotate(getVelocity().heading() - _g.HALF_PI);
		_g.triangle(-vSize / 2, -vSize / 2, 0, vSize / 2, vSize / 2, -vSize / 2);
		_g.popMatrix();
		
		_g.noFill();
		_g.stroke(255.0f, 20.0f);
		Vec2D visualVelocity = getVelocity().getNormalized().scale(15.0f);
		visualVelocity.addSelf(getPosition());
		_g.line(getPosition().x, getPosition().y, visualVelocity.x, visualVelocity.y);
		
		Vec2D futurePos = new Vec2D(getVelocity().scale(getPathFollowLookahead()).add(getPosition()));
		_g.line(getPosition().x, getPosition().y, futurePos.x, futurePos.y);
	}

	public void renderApproachTarget(PGraphics _g)
	{
		_g.noStroke();
		_g.fill(255.0f);
		_g.pushMatrix();
		_g.translate(getPosition().x, getPosition().y);
		_g.rotate(getVelocity().heading() - _g.HALF_PI);
		_g.triangle(-vSize / 2, -vSize / 2, 0, vSize / 2, vSize / 2, -vSize / 2);
		_g.popMatrix();
		
		_g.noFill();
		_g.stroke(255.0f, 20.0f);
		Vec2D visualVelocity = getVelocity().getNormalized().scale(15.0f);
		visualVelocity.addSelf(getPosition());
		_g.line(getPosition().x, getPosition().y, visualVelocity.x, visualVelocity.y);
		
//		_g.stroke(255.0f, 20.0f);
//		_g.line(getPosition().x, getPosition().y, target.x, target.y);
		_g.fill(255.0f);
		_g.noStroke();
		_g.ellipse(target.x, target.y, 4, 4);
		
	}
	
	public void renderWander(PGraphics _g)
	{
		_g.noStroke();
		_g.fill(255.0f);
		_g.pushMatrix();
		_g.translate(getPosition().x, getPosition().y);
		_g.rotate(getVelocity().heading() - _g.HALF_PI);
		
		_g.triangle(-vSize / 2, -vSize / 2, 0, vSize / 2, vSize / 2, -vSize / 2);
		
		_g.noFill();
		_g.stroke(255.0f, 20.0f);
		_g.ellipse(0, getProperties().getWanderDistance(), getProperties().getWanderRadius() * 2, getProperties().getWanderRadius() * 2);
		_g.fill(255.0f, 20.0f);
		_g.noStroke();
		_g.ellipse(0, getProperties().getWanderDistance(), 4, 4);
		_g.popMatrix();
		
		_g.stroke(255.0f, 20.0f);
		_g.line(getPosition().x, getPosition().y, getWanderTarget().x, getWanderTarget().y);
		_g.ellipse(getWanderTarget().x, getWanderTarget().y, 4, 4);
	}

	public String toString()
	{
		String m = "";
		switch (mode)
		{
			case WANDER:
				m = "Wander";
				break;
			case SEEK:
				m = "Seek";
				break;
			case ARRIVE:
				m = "Arrive";
				break;
			case FLEE:
				m = "Flee";
				break;
			case AVOID:
				m = "Avoid";
				break;
		}

		return "Boid (" + m + ") x:" + getPosition().x + " y:" + getPosition().y;
	}
}
